package Java_Lab.Multithreading.Buffer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Buffer<T> {
	private int numberOfElements;
    private int capacity;
    private final List<T> buff;
    private int first;
    private int last;

    public Buffer(int capacity){
    	this.capacity = capacity;
        buff = new ArrayList<T>(capacity);
        for(int i = 0; i < capacity; i++){
        	buff.add(null);
        }
    }

    public synchronized void add(T t){
        while (isFull()){
            try {
                wait();
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        buff.set(last, t);
        lastForward();
        numberOfElements++;

        for(int i = 0; i < capacity; i++){
            System.out.print(buff.get(i) + " ");
        }
        System.out.println(" - write ");

        notifyAll();
    }

    public T peek(){
        return buff.get(first);
    }

    public synchronized T poll(){
        while (isEmpty()){
            try {
                wait();
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        T res = buff.get(first);
        numberOfElements--;
        firstForward();
        notifyAll();

        for(int i = 0; i < capacity; i++){
            System.out.print(buff.get(i) + " ");
        }
        System.out.println(" - read ");

        return res;
    }

    public boolean isFull(){
        return numberOfElements == capacity;
    }

    public boolean isEmpty(){
        return numberOfElements == 0;
    }

    private int firstForward(){
        if(first == capacity - 1) first = 0;
        else first++;
        return first;
    }

    private int lastForward(){
        if(last == capacity - 1) last = 0;
        else last++;
        return last;
    }
    
    public static void main(String[] str){
        Buffer<Integer> buffer = new Buffer<Integer>(4);
        Integer[] integers = {3,13,25,45,53,62};
        new Thread(new Producer<Integer>(buffer, integers)).start();
        new Thread(new Consumer<Integer>(buffer)).start();
        new Thread(new Consumer<Integer>(buffer)).start();
    }
}

class Producer<T> implements Runnable{
    List<T> data;
    Buffer<T> buffer;

    public Producer(Buffer<T> buffer, T[] data){
        this.buffer = buffer;
		this.data = new ArrayList<T>(Arrays.asList(data));
    }

    public void run() {
        for(int i = 0; i < data.size(); i++){
            buffer.add(data.get(i));
        }
    }
}

class Consumer<T> implements Runnable{
    Buffer<T> buffer;

    public Consumer(Buffer<T> buffer){
        this.buffer = buffer;
    }

    public void run() {
        while (!buffer.isEmpty()){
            System.out.println(buffer.poll());
        }
    }
}